home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / ohlutil / mv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-08-24  |  12.9 KB  |  574 lines

  1. /* mv -- move or rename files
  2.    Copyright (C) 1986, 1989, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /*
  19.  * MS-DOS port (c) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
  20.  *
  21.  * To this port, the same copying conditions apply as to the
  22.  * original release.
  23.  *
  24.  * IMPORTANT:
  25.  * This file is not identical to the original GNU release!
  26.  * You should have received this code as patch to the official
  27.  * GNU release.
  28.  *
  29.  * MORE IMPORTANT:
  30.  * This port comes with ABSOLUTELY NO WARRANTY.
  31.  *
  32.  * $Header: e:/gnu/fileutil/RCS/mv.c'v 1.3.0.3 90/06/29 00:47:04 tho Stable $
  33.  */
  34.  
  35. /* Options:
  36.    -i (+interactive)
  37.    Causes mv to require confirmation from the user before
  38.    performing any move that would destroy an existing file.
  39.  
  40.    -f (+force)
  41.    Causes mv to assume a 'y' answer to all questions it would normally ask
  42.    (and not ask the questions).
  43.  
  44.    If -f is not given, the user is queried when a move would
  45.    destroy an existing file whose mode prohibits writing, unless
  46.    stdin is not a tty, in which case the file is skipped.
  47.  
  48.    This test is slightly different from the test 4.2BSD mv makes; 4.2 mv
  49.    merely checks whether the user can write the file.  This actually checks
  50.    the write bits in the mode.  The difference shows when super-user tries
  51.    to mv onto a file with no write bits set.
  52.  
  53.    -v (+verbose)
  54.    List the name of each file as it is moved, and the name it is moved to.
  55.  
  56.    Written by Mike Parker and David MacKenzie */
  57.  
  58. #include <stdio.h>
  59. #include <errno.h>
  60. #include <sys/types.h>
  61. #include "system.h"
  62. #include "getopt.h"
  63. #include "backupfile.h"
  64.  
  65. #ifdef STDC_HEADERS
  66. #include <stdlib.h>
  67. #else
  68. char *getenv ();
  69.  
  70. extern int errno;
  71. #endif
  72.  
  73. #ifdef MSDOS
  74.  
  75. #include <io.h>
  76. #include <malloc.h>
  77.  
  78. #define unlink(name)        force_unlink (name)
  79. static  int force_unlink (char *filename);
  80.  
  81. extern  void main (int argc, char **argv);
  82. extern  void error (int status, int errnum, char *message, ...);
  83. extern  int eaccess_stat (struct stat *statp, int mode);
  84. extern  enum backup_type get_version (char *version);
  85. static  int isdir (char *fn);
  86. static  int movefile (char *from, char *to);
  87. static  int do_move (char *from, char *to);
  88. static  int copy (char *from, char *to);
  89. static  int yesno (void );
  90. static  void strip_trailing_slashes (char *path);
  91. static  void usage (void );
  92.  
  93. #else /* not MSDOS */
  94.  
  95. enum backup_type get_version ();
  96. int copy ();
  97. int do_move ();
  98. int movefile ();
  99. int yesno ();
  100. void error ();
  101. void strip_trailing_slashes ();
  102. void usage ();
  103.  
  104. #endif /* not MSDOS */
  105.  
  106. /* The name this program was run with. */
  107. char *program_name;
  108.  
  109. /* If nonzero, query the user before overwriting files. */
  110. int interactive;
  111.  
  112. /* If nonzero, override as much protection as possible. */
  113. int force;
  114.  
  115. /* If nonzero, list each file as it is moved. */
  116. int verbose;
  117.  
  118. /* If nonzero, stdin is not a tty. */
  119. int stdin_not_tty;
  120.  
  121. /* Return nonzero if FN is a directory or a symlink to a directory,
  122.    zero if not. */
  123. int
  124. isdir (fn)
  125.      char *fn;
  126. {
  127.   struct stat stats;
  128.  
  129.   return (stat (fn, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR);
  130. }
  131.  
  132. struct option long_options[] =
  133. {
  134.   {"backup", 0, NULL, 'b'},
  135.   {"force", 0, &force, 1},
  136.   {"interactive", 0, &interactive, 1},
  137.   {"suffix", 1, NULL, 'S'},
  138.   {"verbose", 0, &verbose, 1},
  139.   {"version-control", 1, NULL, 'V'},
  140.   {NULL, 0, NULL, 0}
  141. };
  142.  
  143. void
  144. main (argc, argv)
  145.      int argc;
  146.      char **argv;
  147. {
  148.   int c;
  149.   int ind;
  150.   int errors;
  151.   int make_backups = 0;
  152.   char *version;
  153.  
  154.   version = getenv ("SIMPLE_BACKUP_SUFFIX");
  155.   if (version)
  156.     simple_backup_suffix = version;
  157.   version = getenv ("VERSION_CONTROL");
  158.   program_name = argv[0];
  159. #ifdef MSDOS
  160.   strlwr (program_name);
  161. #endif /* MSDOS */
  162.   interactive = force = verbose = 0;
  163.   errors = 0;
  164.  
  165.   while ((c = getopt_long (argc, argv, "bfivS:V:", long_options, &ind))
  166.      != EOF)
  167.     {
  168.       if (c == 0 && long_options[ind].flag == NULL)
  169.     c = long_options[ind].val;
  170.       switch (c)
  171.     {
  172.     case 0:
  173.       break;
  174.     case 'b':
  175.       make_backups = 1;
  176.       break;
  177.     case 'f':
  178.       force = 1;
  179.       break;
  180.     case 'i':
  181.       interactive = 1;
  182.       break;
  183.     case 'v':
  184.       verbose = 1;
  185.       break;
  186.     case 'S':
  187.       simple_backup_suffix = optarg;
  188.       break;
  189.     case 'V':
  190.       version = optarg;
  191.       break;
  192.     default:
  193.       usage ();
  194.     }
  195.     }
  196.   if (argc < optind + 2)
  197.     usage ();
  198.  
  199.   if (make_backups)
  200.     backup_type = get_version (version);
  201.  
  202.   strip_trailing_slashes (argv[argc - 1]);
  203.  
  204.   if (argc > optind + 2 && !isdir (argv[argc - 1]))
  205.     error (1, 0, "when moving multiple files, last argument must be a directory");
  206.  
  207.   if (interactive)
  208.     force = 0;
  209.   stdin_not_tty = !isatty (0);
  210.  
  211.   /* Move each arg but the last onto the last. */
  212.   for (; optind < argc - 1; ++optind)
  213.     errors |= movefile (argv[optind], argv[argc - 1]);
  214.  
  215.   exit (errors);
  216. }
  217.  
  218. /* Move file FROM onto TO.  Handles the case when TO is a directory.
  219.    Return 0 if successful, 1 if an error occurred.  */
  220. int
  221. movefile (from, to)
  222.      char *from;
  223.      char *to;
  224. {
  225. #ifdef MSDOS
  226.   strlwr (from);
  227.   strlwr (to);
  228. #endif /* MSDOS */
  229.  
  230.   strip_trailing_slashes (from);
  231.   if (isdir (to))
  232.     {
  233.       /* Target is a directory; build full target filename. */
  234.       char *cp;
  235.       char *newto;
  236.  
  237.       cp = rindex (from, '/');
  238.       if (cp)
  239.     cp++;
  240.       else
  241.     cp = from;
  242.  
  243.       newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
  244.       sprintf (newto, "%s/%s", to, cp);
  245.       return do_move (from, newto);
  246.     }
  247.   else
  248.     return do_move (from, to);
  249. }
  250.  
  251. struct stat to_stats, from_stats;
  252.  
  253. /* Move FROM onto TO.  Handles cross-filesystem moves.
  254.    If TO is a directory, FROM must be also.
  255.    Return 0 if successful, 1 if an error occurred.  */
  256. int
  257. do_move (from, to)
  258.      char *from;
  259.      char *to;
  260. {
  261.   char *to_backup = NULL;
  262.  
  263.   if (lstat (from, &from_stats) != 0)
  264.     {
  265.       error (0, errno, "%s", from);
  266.       return 1;
  267.     }
  268.  
  269.   if (verbose)
  270.     printf ("  %s -> %s\n", from, to);
  271.  
  272.   if (lstat (to, &to_stats) == 0)
  273.     {
  274.  
  275. #ifndef MSDOS
  276.       if (from_stats.st_dev == to_stats.st_dev
  277.       && from_stats.st_ino == to_stats.st_ino)
  278.     {
  279.       error (0, 0, "`%s' and `%s' are the same file", from, to);
  280.       return 1;
  281.     }
  282. #endif /* MSDOS */
  283.  
  284.       if ((to_stats.st_mode & S_IFMT) == S_IFDIR)
  285.     {
  286.       error (0, 0, "%s: cannot overwrite directory", to);
  287.       return 1;
  288.     }
  289.       if (interactive)
  290.     {
  291.       fprintf (stderr, "%s: replace `%s'? ", program_name, to);
  292.       if (!yesno ())
  293.         return 0;
  294.     }
  295.       else if (!force)
  296.     {
  297.       int may_overwrite;
  298.  
  299.       /* Treat the file as nonwritable if it lacks write permission bits,
  300.          even if we are root.  */
  301. #ifdef S_IFLNK
  302.       if ((to_stats.st_mode & S_IFMT) == S_IFLNK)
  303.         may_overwrite = 1;
  304.       else
  305. #endif
  306.         may_overwrite = eaccess_stat (&to_stats, W_OK) == 0
  307.           && (to_stats.st_mode & 0222);
  308.  
  309.       if (!may_overwrite)
  310.         {
  311.           if (stdin_not_tty)
  312.         {
  313.           error (0, 0, "%s: no write permission", to);
  314.           return 1;
  315.         }
  316.           fprintf (stderr, "%s: override mode %04o for `%s'? ",
  317.                program_name, to_stats.st_mode & 0777, to);
  318.           if (!yesno ())
  319.         return 0;
  320.         }
  321.     }
  322.  
  323.       if (backup_type != none)
  324.     {
  325.       to_backup = find_backup_file_name (to);
  326.       if (to_backup == NULL)
  327.         error (1, 0, "virtual memory exhausted");
  328. #ifdef MSDOS
  329.       if (unlink (to_backup) || rename (to, to_backup))
  330. #else /* not MSDOS */
  331.       if (rename (to, to_backup))
  332. #endif /* not MSDOS */
  333.         {
  334.           if (errno != ENOENT)
  335.         {
  336.           error (0, errno, "cannot backup `%s'", to);
  337.           free (to_backup);
  338.           return 1;
  339.         }
  340.           else
  341.         {
  342.           free (to_backup);
  343.           to_backup = NULL;
  344.         }
  345.         }
  346.     }
  347.     }
  348.   else if (errno != ENOENT)
  349.     {
  350.       error (0, errno, "%s", to);
  351.       return 1;
  352.     }
  353.  
  354. #ifdef MSDOS
  355.   if (unlink (to) == 0 && rename (from, to) == 0)
  356. #else /* not MSDOS */
  357.   if (rename (from, to) == 0)
  358. #endif /* not MSDOS */
  359.     {
  360.       if (to_backup)
  361.     free (to_backup);
  362.       return 0;
  363.     }
  364.  
  365.   if (errno != EXDEV)
  366.     {
  367.       error (0, errno, "cannot move `%s' to `%s'", from, to);
  368.       goto un_backup;
  369.     }
  370.  
  371.   /* rename failed on cross-filesystem link.  Copy the file instead. */
  372.  
  373.   if (copy (from, to))
  374.     goto un_backup;
  375.   
  376.   if (to_backup)
  377.     free (to_backup);
  378.  
  379.   if (unlink (from))
  380.     {
  381.       error (0, errno, "cannot remove `%s'", from);
  382.       return 1;
  383.     }
  384.  
  385.   return 0;
  386.  
  387.  un_backup:
  388.   if (to_backup)
  389.     {
  390. #ifdef MSDOS
  391.       if (unlink (to) || rename (to_backup, to))
  392. #else /* not MSDOS */
  393.       if (rename (to_backup, to))
  394. #endif /* not MSDOS */
  395.     error (0, errno, "cannot un-backup `%s'", to);
  396.       free (to_backup);
  397.     }
  398.   return 1;
  399. }
  400.  
  401. /* Copy file FROM onto file TO.
  402.    Return 1 if an error occurred, 0 if successful. */
  403. int
  404. copy (from, to)
  405.      char *from, *to;
  406. {
  407.   int ifd;
  408.   int ofd;
  409.   char buf[1024 * 8];
  410.   int len;            /* Number of bytes read into `buf'. */
  411.   
  412.   if ((from_stats.st_mode & S_IFMT) != S_IFREG)
  413.     {
  414.       error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
  415.          from);
  416.       return 1;
  417.     }
  418.   
  419.   if (unlink (to) && errno != ENOENT)
  420.     {
  421.       error (0, errno, "cannot remove `%s'", to);
  422.       return 1;
  423.     }
  424.  
  425. #ifdef MSDOS
  426.   ifd = open (from, O_RDONLY | O_BINARY, 0);
  427. #else /* not MSDOS */
  428.   ifd = open (from, O_RDONLY, 0);
  429. #endif /* not MSDOS */
  430.   if (ifd < 0)
  431.     {
  432.       error (0, errno, "%s", from);
  433.       return 1;
  434.     }
  435.  
  436. #ifdef MSDOS
  437.   ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0777);
  438. #else /* not MSDOS */
  439.   ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0777);
  440. #endif /* not MSDOS */
  441.   if (ofd < 0)
  442.     {
  443.       error (0, errno, "%s", to);
  444.       close (ifd);
  445.       return 1;
  446.     }
  447.   if (
  448. #ifdef FCHMOD_MISSING
  449.       chmod (to, from_stats.st_mode & 0777)
  450. #else
  451.       fchmod (ofd, from_stats.st_mode & 0777)
  452. #endif
  453.       )
  454.       {
  455.     error (0, errno, "%s", to);
  456.     close (ifd);
  457.     close (ofd);
  458.     unlink (to);
  459.     return 1;
  460.       }
  461.   
  462.   while ((len = read (ifd, buf, sizeof (buf))) > 0)
  463.     {
  464.       int wrote = 0;
  465.       char *bp = buf;
  466.       
  467.       do
  468.     {
  469.       wrote = write (ofd, bp, len);
  470.       if (wrote < 0)
  471.         {
  472.           error (0, errno, "%s", to);
  473.           close (ifd);
  474.           close (ofd);
  475.           unlink (to);
  476.           return 1;
  477.         }
  478.       bp += wrote;
  479.       len -= wrote;
  480.     } while (len > 0);
  481.     }
  482.   if (len < 0)
  483.     {
  484.       error (0, errno, "%s", from);
  485.       close (ifd);
  486.       close (ofd);
  487.       unlink (to);
  488.       return 1;
  489.     }
  490.   close (ifd);
  491.   close (ofd);
  492.   
  493.   /* Try to copy the old file's modtime and access time.  */
  494.   {
  495.     struct utimbuf tv;
  496.  
  497.     tv.actime = from_stats.st_atime;
  498.     tv.modtime = from_stats.st_mtime;
  499.     if (utime (to, &tv))
  500.       {
  501.     error (0, errno, "%s", to);
  502.     return 1;
  503.       }
  504.   }
  505.   return 0;
  506. }
  507.  
  508. /* Read one line from standard input
  509.    and return nonzero if that line begins with y or Y,
  510.    otherwise return 0. */
  511. int
  512. yesno ()
  513. {
  514.   int c;
  515.   int rv;
  516.  
  517.   fflush (stderr);
  518.   c = getchar ();
  519.   rv = (c == 'y') || (c == 'Y');
  520.   while (c != EOF && c != '\n')
  521.     c = getchar ();
  522.  
  523.   return rv;
  524. }
  525.  
  526. /* Remove trailing slashes from PATH; they cause some system calls to fail. */
  527. void
  528. strip_trailing_slashes (path)
  529.      char *path;
  530. {
  531.   int last;
  532.  
  533.   last = strlen (path) - 1;
  534.   while (last > 0 && path[last] == '/')
  535.     path[last--] = '\0';
  536. }
  537.  
  538. void
  539. usage ()
  540. {
  541.   fprintf (stderr, "\
  542. Usage: %s [-bfiv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  543.        [+backup] [+force] [+interactive] [+verbose] [+suffix backup-suffix]\n\
  544.        [+version-control {numbered,existing,simple}] source dest\n\
  545. \n\
  546.        %s [-bfiv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  547.        [+backup] [+force] [+interactive] [+verbose] [+suffix backup-suffix]\n\
  548.        [+version-control {numbered,existing,simple}] source... directory\n",
  549.        program_name, program_name);
  550.   exit (1);
  551. }
  552.  
  553. #ifdef MSDOS
  554. #undef unlink                    /* nasty tricks ... */
  555.  
  556. int
  557. force_unlink (char *filename)
  558. {
  559.   if (access (filename, 0))    /* file doesn't exist, pretend success */
  560.     return 0;
  561.   else
  562.     {
  563.       if (access (filename, 2)              /* no write permission */
  564.       && chmod (filename, S_IREAD|S_IWRITE))  /* can't force it ...  */
  565.     {
  566.       error (0, errno, "can't force write permission for %s", filename);
  567.       return -1;
  568.     }
  569.       else
  570.     return unlink (filename);
  571.     }
  572. }
  573. #endif /* MSDOS */
  574.